package com.yeskery.nut.bean.condition;

import com.yeskery.nut.annotation.condition.ConditionalOnBean;
import com.yeskery.nut.application.NutApplication;
import com.yeskery.nut.bean.AnnotationApplicationContext;
import com.yeskery.nut.bean.NoSuchBeanException;
import com.yeskery.nut.scan.BeanAnnotationScanMetadata;

import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * bean存在条件判断
 * @author sunjay
 * 2023/9/6
 */
public class OnBeanCondition implements Condition {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(OnBeanCondition.class.getName());

    @Override
    public boolean matches(NutApplication nutApplication, Collection<BeanAnnotationScanMetadata> beanAnnotationScanMetadata, Object conditionalAnnotation) {
        ConditionalOnBean conditionalOnBean = (ConditionalOnBean) conditionalAnnotation;
        boolean result = doMatches(nutApplication, beanAnnotationScanMetadata, conditionalOnBean.value(), conditionalOnBean.name());
        if (!result && logger.isLoggable(Level.FINEST)) {
            logger.log(Level.FINEST, "Bean Condition Not Match, Condition[" + this.getClass().getName()
                    + "], Expect Bean Found[class(" + Arrays.stream(conditionalOnBean.value()).map(Class::getName).collect(Collectors.joining(", "))
                    + "), name(" + String.join(", ", conditionalOnBean.name()) + ")], Actual Bean Not Found.");
        }
        return result;
    }

    /**
     * 判断bean是否匹配的方法
     * @param nutApplication Nut应用对象
     * @param beanAnnotationScanMetadata 所有Bean扫描元数据类型
     * @param classes bean类型数组
     * @param beanNames bean名称数组
     * @return bean是否匹配的方法
     */
    protected boolean doMatches(NutApplication nutApplication, Collection<BeanAnnotationScanMetadata> beanAnnotationScanMetadata,
                                Class<?>[] classes, String[] beanNames) {
        AnnotationApplicationContext annotationApplicationContext = (AnnotationApplicationContext) nutApplication.getApplicationContext();
        for (Class<?> beanClass : classes) {
            if (annotationApplicationContext.getBeans(beanClass).isEmpty()) {
                annotationApplicationContext.registerBean(beanClass, beanAnnotationScanMetadata);
                if (annotationApplicationContext.getBeans(beanClass).isEmpty()) {
                    return false;
                }
            }
        }
        for (String beanName : beanNames) {
            try {
                annotationApplicationContext.getBean(beanName);
            } catch (NoSuchBeanException e) {
                Optional<BeanAnnotationScanMetadata> optional = beanAnnotationScanMetadata.stream()
                        .filter(b -> b.getName().equals(beanName)).findFirst();
                if (!optional.isPresent()) {
                    return false;
                }
                annotationApplicationContext.registerBean(optional.get());
                try {
                    annotationApplicationContext.getBean(beanName);
                } catch (NoSuchBeanException e1) {
                    return false;
                }
            }
        }
        return true;
    }
}
